package tjs.org.springLoader.reloader;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * Created by Nicky.Tang on 2021/9/29 11:40 下午
 *
 * @since 02.12.10
 */
public class WebMvcReloader implements IReloader, ApplicationContextAware, InitializingBean {

    private Method getMappingForMethod;
    private ApplicationContext applicationContext;
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    @Override
    public boolean isReloader(Class<?> changeClass) {
        return changeClass.getAnnotation(RestController.class) != null;
    }

    @Override
    public void reloaderBefore(Class<?> oldClass) {
        //移除旧的requestmapping映射
        for (Method declaredMethod : oldClass.getDeclaredMethods()) {
            RequestMappingInfo requestMappingInfo = null;
            try {
                requestMappingInfo = (RequestMappingInfo) this.getMappingForMethod.invoke(this.requestMappingHandlerMapping, declaredMethod, oldClass);
                this.requestMappingHandlerMapping.unregisterMapping(requestMappingInfo);
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void reloaderAfter(Class<?> newClass) {
        // 重新注册requestmapping映射
        String beanName = applicationContext.getBeanNamesForType(newClass)[0];
        Object bean = applicationContext.getBean(beanName);
        for (Method declaredMethod : bean.getClass().getDeclaredMethods()) {
            RequestMappingInfo requestMappingInfo = null;
            try {
                requestMappingInfo = (RequestMappingInfo) getMappingForMethod.invoke(requestMappingHandlerMapping, declaredMethod, newClass);
                this.requestMappingHandlerMapping.registerMapping(requestMappingInfo, bean, declaredMethod);
            } catch (InvocationTargetException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE + 2;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.requestMappingHandlerMapping = this.applicationContext.getBean(RequestMappingHandlerMapping.class);
        this.getMappingForMethod = this.requestMappingHandlerMapping.getClass().getDeclaredMethod("getMappingForMethod", Method.class, Class.class);
        this.getMappingForMethod.setAccessible(true);
    }
}
